package com.zy.blog.common.param;

import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.zy.blog.common.enums.ConditionEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Author: zy
 * Description: 分页查询 请求参数
 * Date: 2020/5/20
 */
@Data
@Slf4j
@NoArgsConstructor
@AllArgsConstructor
public class PageRequestParam {

    private static Map<Class, SerializedLambda> classLambdaCache = new ConcurrentHashMap<>();

    /**
     * 当前页
     */
    @Positive(message = "pageNum必须是正整数")
    @ApiModelProperty(value = "页码",example = "1")
    private Long pageNum = 1L;

    /**
     * 每页显示条数，默认 10
     */
    @Positive(message = "pageSize必须是正整数")
    @ApiModelProperty(value = "页大小",example = "10")
    private Long pageSize = 10L;

    /**
     * 查询全部
     */
    @NotNull(message = "all不能为空")
    @ApiModelProperty(value = "查询全部",example = "true")
    private Boolean all = true;

    /**
     * 高级查询条件
     */
    @ApiModelProperty("高级查询条件")
    private List<@Valid ConditionParam> conditions;

    /*
     * 追加查询条件
     */
    public PageRequestParam addCondition(String filed, ConditionEnum op, String value) {
        return this.addCondition(new ConditionParam(filed, op, value));
    }

    /*
     * 追加查询条件
     */
    public <T> PageRequestParam addCondition(SFunction<T, ?> filed, ConditionEnum op, String value) {
        return this.addCondition(new ConditionParam(convertToFieldName(filed), op, value));
    }

    /*
     * 追加查询条件
     */
    public PageRequestParam addCondition(ConditionParam condition) {
        initConditions();
        this.conditions.add(condition);
        return this;
    }

    /*
     * 手拼sql
     */
    public PageRequestParam addSqlCondition(String where) {
        initConditions();
        this.conditions.add(new ConditionParam(null, ConditionEnum.APPLY, where));
        return this;
    }

    /*
     * 指定升序字段
     */
    public PageRequestParam addOrderByAsc(String filed) {
        initConditions();
        this.conditions.add(new ConditionParam(filed, ConditionEnum.ORDER_BY_ASC, null));
        return this;
    }

    /*
     * 指定升序字段
     */
    public <T> PageRequestParam addOrderByAsc(SFunction<T, ?> filed) {
        initConditions();
        this.conditions.add(new ConditionParam(convertToFieldName(filed), ConditionEnum.ORDER_BY_ASC, null));
        return this;
    }

    /*
     * 指定降序字段
     */
    public PageRequestParam addOrderByDesc(String filed) {
        initConditions();
        this.conditions.add(new ConditionParam(filed, ConditionEnum.ORDER_BY_DESC, null));
        return this;
    }

    /*
     * 指定降序字段
     */
    public <T> PageRequestParam addOrderByDesc(SFunction<T, ?> filed) {
        initConditions();
        this.conditions.add(new ConditionParam(convertToFieldName(filed), ConditionEnum.ORDER_BY_DESC, null));
        return this;
    }

    /*
     * 初始化条件列表
     */
    private void initConditions() {
        if (this.conditions == null) {
            this.conditions = new ArrayList<>();
        }
    }


    /***
     * 转换方法引用为属性名
     * @param fn
     * @return
     */
    private static <T> String convertToFieldName(SFunction<T,?> fn) {

        SerializedLambda lambda;

        try {
            lambda = getSerializedLambda(fn);
        } catch (Exception e) {
            log.error(e.getMessage(),e);
            return "";
        }

        // 获取方法名
        String methodName = lambda.getImplMethodName();

        //提取方法名前缀
        String prefix = null;
        if (methodName.startsWith("get")) {
            prefix = "get";
        } else if (methodName.startsWith("is")) {
            prefix = "is";
        }
        if (prefix == null) {
            log.error("无效的getter方法: " + methodName);
            return methodName;
        }
        // 截取get/is之后的字符串并转换首字母为小写
        return toLowerCaseFirstOne(methodName.replace(prefix, ""));
    }

    /**
     * 首字母转小写
     * @param s
     * @return
     */
    private static String toLowerCaseFirstOne(String s) {
        if (Character.isLowerCase(s.charAt(0))) {
            return s;
        } else {
            return Character.toLowerCase(s.charAt(0)) + s.substring(1);
        }
    }

    /**
     * 关键在于这个方法
     */
    private static SerializedLambda getSerializedLambda(Serializable fn) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        SerializedLambda lambda = classLambdaCache.get(fn.getClass());

        // 先检查缓存中是否已存在
        if (lambda == null) {
            // 提取SerializedLambda并缓存
            Method method = fn.getClass().getDeclaredMethod("writeReplace");
            method.setAccessible(true);
            lambda = (SerializedLambda) method.invoke(fn);
            classLambdaCache.put(fn.getClass(), lambda);
        }

        return lambda;
    }
}
